/*
 * Copyright the original author or authors.
 * 
 * Licensed under the MOZILLA PUBLIC LICENSE, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.mozilla.org/MPL/MPL-1.1.html
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.pixlib.sfe.display 
{
	import net.pixlib.commands.Delegate;
	import net.pixlib.events.StringEvent;
	import net.pixlib.plugin.Plugin;
	import net.pixlib.sfe.event.EventList;
	import net.pixlib.sfe.event.FontEvent;
	import net.pixlib.sfe.log.Debug;
	import net.pixlib.sfe.structure.FontProperties;
	import net.pixlib.sfe.util.PrefNameList;
	import net.pixlib.sfe.util.getPreferences;
	import net.pixlib.sfe.util.getTranslation;
	import net.pixlib.tms.LocalManager;
	import net.pixlib.tms.LocalManagerEvent;
	import net.pixlib.utils.StringUtils;

	import org.aswing.ASColor;
	import org.aswing.AsWingConstants;
	import org.aswing.AssetIcon;
	import org.aswing.BorderLayout;
	import org.aswing.Insets;
	import org.aswing.JCheckBox;
	import org.aswing.JLabel;
	import org.aswing.JLabelButton;
	import org.aswing.JOptionPane;
	import org.aswing.JPanel;
	import org.aswing.JTextField;
	import org.aswing.SoftBox;
	import org.aswing.SoftBoxLayout;
	import org.aswing.border.EmptyBorder;
	import org.aswing.border.LineBorder;
	import org.aswing.event.FrameEvent;

	import flash.desktop.ClipboardFormats;
	import flash.desktop.NativeDragManager;
	import flash.events.Event;
	import flash.events.FocusEvent;
	import flash.events.NativeDragEvent;
	import flash.filesystem.File;
	import flash.net.FileFilter;
	import flash.ui.Keyboard;

	/**
	 * @author Romain Ecarnot
	 */
	public class FontPanel extends JPanel
	{

		//--------------------------------------------------------------------
		// Private properties
		//--------------------------------------------------------------------
		[Embed(source="plus.png")]
		private var PlusPng : Class; 

		[Embed(source="trash.png")]
		private var TrashPng : Class; 

		[Embed(source="browse.png")]
		private var BrowsePng : Class; 

		[Embed(source="unicode.png")]
		private var UnicodePng : Class; 

		/** @private */
		private var _fileInput : JTextField;

		/** @private */
		private var _identifierInput : JTextField; 

		/** @private */
		private var _browseButton : JLabelButton; 

		/** @private */
		private var _checkBold : JCheckBox; 

		/** @private */
		private var _checkItalic : JCheckBox; 

		/** @private */
		private var _unicodeButton : JLabelButton; 

		/** @private */
		private var _copyButton : JLabelButton; 

		/** @private */
		private var _delButton : JLabelButton; 

		/** @private */
		private var _file : File; 

		/** @private */
		private var _owner : Plugin; 

		/** @private */
		private var _key : String;
		
		[ArrayElementType("int")]
		/** @private */
		private var _fontUnicode : Array; 

		/** @private */
		private var _unicodeView : SoftBox; 

		
		//--------------------------------------------------------------------
		// Public properties
		//--------------------------------------------------------------------
		
		/**
		 * The Font application identifier (unique for Model data)
		 */
		public function get id() : String
		{
			return _key;
		}

		/**
		 * The Font name.
		 * 
		 * <p>Font name is the identifier to use in our ActionScript code to 
		 * use this embed font.</p>
		 */
		public function get fontName() : String
		{
			return _identifierInput.getText();
		}

		/**
		 * The font file path.
		 */
		public function get fontFile() : String
		{
			return _fileInput.getText();
		}		

		/**
		 * The font 'bold' property.
		 */
		public function get fontBold() : Boolean
		{
			return _checkBold.isSelected();
		}

		/**
		 * The font 'italic' property.
		 */
		public function get fontItalic() : Boolean
		{
			return _checkItalic.isSelected();
		}

		/**
		 * The font unicode range property.
		 */
		public function get fontUnicode() : Array
		{
			return _fontUnicode;
		}

		
		//--------------------------------------------------------------------
		// Public API
		//--------------------------------------------------------------------
		
		/**
		 * Constructs new FontPanel instance.
		 */
		public function FontPanel( owner : Plugin, font : FontProperties)
		{
			super(new BorderLayout(5, 0));
			
			_owner = owner;
			
			init();
			
			update(font);
		}

		/**
		 * Updates current font definition using pased-in font value object.
		 * 
		 * @param font	Font value object with updated informations
		 */
		public function update(font : FontProperties) : void
		{
			_key = font.id;
			
			_fileInput.setText(font.path);
			_identifierInput.setText(font.name);
			_checkBold.setSelected(font.bold);
			_checkItalic.setSelected(font.italic);
			_fontUnicode = font.unicode;
			
			_displayUnicode();
		}

		/**
		 * Sets focus to font identifier text input.
		 */
		public function setFocus() : void
		{
			_identifierInput.requestFocus();
			_identifierInput.setSelection(0, _identifierInput.getText().length);
		}

		
		//--------------------------------------------------------------------
		// Protected methods
		//--------------------------------------------------------------------
			
		/**
		 * Inits view.
		 */
		protected function init() : void
		{
			LocalManager.getInstance().addEventListener(LocalManagerEvent.onLocalChangeEVENT, _onLocalChangeHandler);
			
			_fontUnicode = [];
			
			_file = new File();
			
			_identifierInput = new JTextField();
			_identifierInput.setToolTipText("Font identifier");
			_identifierInput.setPreferredWidth(80);
			_identifierInput.addEventListener(FocusEvent.FOCUS_OUT, _onIdentifierFocusOutHandler);
			
			_fileInput = new JTextField();
			_fileInput.setEditable(false);
			_fileInput.setFocusable(false);
			_fileInput.addEventListener(NativeDragEvent.NATIVE_DRAG_ENTER, _onDragEnterHandler);
			_fileInput.addEventListener(NativeDragEvent.NATIVE_DRAG_DROP, _onDragDropHandler);
			_fileInput.addEventListener(NativeDragEvent.NATIVE_DRAG_EXIT, _onDragExitHandler);
   						_browseButton = new JLabelButton("", new AssetIcon(new BrowsePng()));
			_browseButton.setToolTipText("choose font file");
			_browseButton.setPreferredWidth(25);
			_browseButton.addActionListener(_onBrowseClickHandler);
			
			var fileLine : JPanel = new JPanel(new BorderLayout(5, 5));
			fileLine.append(_fileInput, BorderLayout.CENTER);
			fileLine.append(_browseButton, BorderLayout.EAST);
			
			_checkBold = new JCheckBox("bold");
			_checkBold.addActionListener(_onUpdateFontHandler);
			
			_checkItalic = new JCheckBox("italic");
			_checkItalic.addActionListener(_onUpdateFontHandler);
			
			_unicodeButton = new JLabelButton("", new AssetIcon(new UnicodePng()));
			_unicodeButton.setToolTipText("Unicode range");
			_unicodeButton.setPreferredWidth(25);
			_unicodeButton.addActionListener(_onEditUnicodeHandler);
			
			_copyButton = new JLabelButton("", new AssetIcon(new PlusPng()));
			_copyButton.setToolTipText("add new font");
			_copyButton.setPreferredWidth(25);
			_copyButton.addActionListener(_onAddClickHandler);
			
			_delButton = new JLabelButton("", new AssetIcon(new TrashPng()));
			_delButton.setToolTipText("remove this font");
			_delButton.setPreferredWidth(25);
			_delButton.addActionListener(_onRemoveClickHandler);
			
			var options : SoftBox = new SoftBox(SoftBoxLayout.X_AXIS, 5, AsWingConstants.LEFT);
			options.append(_checkBold);
			options.append(_checkItalic);			options.append(_unicodeButton);			options.append(_copyButton);			options.append(_delButton);
			
			var p : JPanel = new JPanel(new BorderLayout(5, 5));
			p.setBorder(new EmptyBorder(null, new Insets(5, 5, 5, 5)));
			p.append(_identifierInput, BorderLayout.WEST);			p.append(fileLine, BorderLayout.CENTER);
			p.append(options, BorderLayout.EAST);
			
			_unicodeView = new SoftBox(SoftBoxLayout.Y_AXIS, 0, AsWingConstants.LEFT);
			_unicodeView.setBorder(new EmptyBorder(null, new Insets(0, 20, 0, 0)));
			
			UnicodeSelector.getInstance().addEventListener(UnicodeSelector.onTranslatedEVENT, _onUnicodeLocalChangedHandler);
			
			setBorder(new LineBorder(null, ASColor.BLACK, 1, 0));
			
			append(p, BorderLayout.CENTER);			append(_unicodeView, BorderLayout.SOUTH);
			
			_onLocalChangeHandler();
		}

		
		//--------------------------------------------------------------------
		// Private methods
		//--------------------------------------------------------------------
		
		/** @private */
		private function _onLocalChangeHandler(event : Event = null) : void
		{
			_identifierInput.setToolTipText(getTranslation("fontIDTip"));
			_browseButton.setToolTipText(getTranslation("fontBrowse"));			_checkBold.setText(getTranslation("fontBold"));			_checkItalic.setText(getTranslation("fontItalic"));			_unicodeButton.setToolTipText(getTranslation("fontRange"));
			_copyButton.setToolTipText(getTranslation("fontAdd"));			_delButton.setToolTipText(getTranslation("fontRemove"));
			
			_displayUnicode();
		}

		/** 
		 * Triggered when user click on "Unicode range" button.
		 * 
		 * @param event	AWEvent event
		 */
		private function _onEditUnicodeHandler(event : Event) : void
		{
			UnicodeSelector.getInstance().addEventListener(FrameEvent.FRAME_CLOSING, _onChangeUnicodeHandler);
			UnicodeSelector.getInstance().open(fontUnicode);
		}

		/** @private */
		private function _onChangeUnicodeHandler(event : Event) : void
		{
			UnicodeSelector.getInstance().removeEventListener(FrameEvent.FRAME_CLOSING, _onChangeUnicodeHandler);
			
			_fontUnicode = UnicodeSelector.getInstance().getSelection();
			
			_displayUnicode();
			
			_updateModel();
		}

		/** @private */
		private function _onUnicodeLocalChangedHandler(event : Event) : void
		{
			_displayUnicode();
		}

		/** 
		 * Triggered when user click on "add font" button.
		 * 
		 * @param event	AWEvent event
		 */
		private function _onAddClickHandler(event : Event) : void
		{
			var vo : FontProperties = new FontProperties();
			vo.name = fontName;
			vo.path = fontFile;
			vo.unicode = fontUnicode.concat([]);
			
			_owner.firePrivateEvent(new FontEvent(EventList.ADD_FONT, vo));
		}

		/** 
		 * Triggered when user click on "remove font" button.
		 * 
		 * @param event	AWEvent event
		 */
		private function _onRemoveClickHandler(event : Event) : void
		{
			if(getKeyboardManager().isKeyDown(Keyboard.SHIFT))
			{
				_onConfirmRemoveHandler(JOptionPane.YES);
			}
			else
			{
				JOptionPane.showMessageDialog(getTranslation("warning"), getTranslation("fontRemovePrevent"), _onConfirmRemoveHandler, this, true, null, JOptionPane.YES | JOptionPane.NO);
			}
		}

		/** 
		 * Triggered when user confirm (or not) font removing.
		 * 
		 * @param buttonID	Clicked button ID
		 */
		private function _onConfirmRemoveHandler(buttonID : int) : void
		{
			if(buttonID == JOptionPane.YES)
			{
				_identifierInput.removeEventListener(FocusEvent.FOCUS_OUT, _onIdentifierFocusOutHandler);
				_fileInput.removeEventListener(NativeDragEvent.NATIVE_DRAG_ENTER, _onDragEnterHandler);
				_fileInput.removeEventListener(NativeDragEvent.NATIVE_DRAG_DROP, _onDragDropHandler);
				_fileInput.removeEventListener(NativeDragEvent.NATIVE_DRAG_EXIT, _onDragExitHandler);
				_browseButton.removeActionListener(_onBrowseClickHandler);
				_checkBold.removeActionListener(_onUpdateFontHandler);
				_checkItalic.removeActionListener(_onUpdateFontHandler);
				_unicodeButton.removeActionListener(_onEditUnicodeHandler);
				_copyButton.removeActionListener(_onAddClickHandler);
				_delButton.removeActionListener(_onRemoveClickHandler);
				
				LocalManager.getInstance().removeEventListener(LocalManagerEvent.onLocalChangeEVENT, _onLocalChangeHandler);
				UnicodeSelector.getInstance().removeEventListener(UnicodeSelector.onTranslatedEVENT, _onUnicodeLocalChangedHandler);
				
				_owner.firePrivateEvent(new StringEvent(EventList.REMOVE_FONT, this, _key));
			}
		}	

		/** 
		 * Triggered when user click on "browse font" button.
		 * 
		 * @param event	MouseEvent event
		 */
		private function _onBrowseClickHandler(event : Event) : void
		{
			var fontFilter : FileFilter = new FileFilter(getTranslation("fontFileFilter"), "*.ttf;*.TTF;*.otf;*.OTF");
			
			try 
			{
				if(fontFile.length > 0)
				{
					_file = new File(fontFile);
				}
				else 
				{
					_file = new File(getPreferences().getValue(PrefNameList.FONT_PATH, File.documentsDirectory.nativePath));
				}
				
				_file.browseForOpen(getTranslation("fontFileBrowse"), [fontFilter]);
				_file.addEventListener(Event.SELECT, _onFontSelectedHandler);
			}
			catch (error : Error)
			{
				Debug.ERROR("Failed: " + error.message);
				_file.removeEventListener(Event.SELECT, _onFontSelectedHandler);
			}
		}

		/**
		 * Triggered when a font file is selected.
		 * 
		 * @param event	Selected font file
		 */
		private function _onFontSelectedHandler(event : Event) : void
		{
			_file.removeEventListener(Event.SELECT, _onFontSelectedHandler);
			
			_fileInput.setText(File(event.target).nativePath);
			
			_updateModel();
		}

		/**
		 * Triggered when a Drag operation starts.
		 * 
		 * @param event	NativeDragEvent instance
		 */
		private function _onDragEnterHandler(event : NativeDragEvent) : void
		{
			NativeDragManager.acceptDragDrop(_fileInput);
		}

		/**
		 * Triggered when a Drop operation starts.
		 * 
		 * @param event	NativeDragEvent instance
		 */
		private function _onDragDropHandler(event : NativeDragEvent) : void
		{
			var files : Array = event.clipboard.getData(ClipboardFormats.FILE_LIST_FORMAT) as Array;
			
			for each (var file:File in files)
			{
				switch (file.extension)
				{
					case "otf" : 
					case "OTF" : 
					case "ttf" : 
					case "TTF" : 
						_onConfirmDropHandler(JOptionPane.YES, file.nativePath);
						break;
					default :
						JOptionPane.showMessageDialog(getTranslation("warning"), getTranslation("fontUnknownExt"), Delegate.create(_onConfirmDropHandler, file.nativePath), this, true, null, JOptionPane.YES | JOptionPane.NO);
				}
			}
		}
		
		/**
		 * Triggered when a Drop operation is currently running and dropped file extension 
		 * is unknown for application rules.
		 * 
		 * <p>If file extension is unknown, a Dialog box is opened to ask for operation.</p>
		 * 
		 * @param buttonID		Dialog clicked button ID
		 * @param nativePath	Selected file path
		 */
		private function _onConfirmDropHandler(buttonID : int, nativePath : String) : void
		{
			if(buttonID == JOptionPane.YES)
			{
				_fileInput.setText(nativePath);
				_onUpdateFontHandler();
			}	
		}

		/**
		 * Triggered when a Drop operation is finished.
		 * 
		 * @param event	NativeDragEvent instance
		 */
		private function _onDragExitHandler(event : NativeDragEvent) : void
		{
			Debug.DEBUG("onDragExitHandler()", this);
		}

		/**
		 * Triggered when Font identifier text input is left.
		 */
		private function _onIdentifierFocusOutHandler(event : FocusEvent) : void
		{
			_identifierInput.setText(StringUtils.trim(_identifierInput.getText()));
			
			_updateModel();
		}

		/**
		 * Call for model update.
		 */
		private function _onUpdateFontHandler(event : Event = null) : void
		{
			_updateModel();
		}

		/**
		 * Updates model with current font definition.
		 */
		private function _updateModel() : void
		{
			var font : FontProperties = new FontProperties();
			font.id = _key;
			font.name = _identifierInput.getText();
			font.path = _fileInput.getText();
			font.bold = _checkBold.isSelected();
			font.italic = _checkItalic.isSelected();
			font.unicode = fontUnicode;
			
			_owner.firePrivateEvent(new FontEvent(EventList.UPDATE_FONT, font));
		}

		/** @private */
		private function _displayUnicode() : void
		{
			_unicodeView.removeAll();
			
			var len : uint = _fontUnicode.length;
			for(var i : uint = 0;i < len; i++)
			{
				_unicodeView.append(new JLabel("- " + UnicodeSelector.getInstance().getLabelAt(fontUnicode[i]), null, JLabel.LEFT));	
			}
		}
	}
}
